package com.baofu.paycore.biz.paycore.handler.impl;

import com.baofu.paycore.biz.engine.command.Command;
import com.baofu.paycore.biz.engine.command.impl.BankCommand;
import com.baofu.paycore.biz.engine.processor.BaseProcessor;
import com.baofu.paycore.biz.engine.result.CommandResult;
import com.baofu.paycore.biz.model.WebPayBO;
import com.baofu.paycore.biz.paycore.converter.BO2BOConverter;
import com.baofu.paycore.common.exception.ServiceErrorCode;
import com.baofu.paycore.manager.model.PayBO;
import com.baofu.paycore.manager.model.RechargeBO;
import com.baofu.paycore.manager.model.SubPayBO;
import com.baofu.paycore.service.facade.enums.PayStatusEnums;
import com.baofu.paycore.service.facade.enums.PayToolRouteEnum;
import com.baofu.paycore.service.facade.model.WebPayResDTO;
import com.system.commons.exception.BizServiceException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * web支付
 * <p>
 *     1 异步支付
 *     2 指令完成后处理
 *     3 生成指令
 * </p>
 * User: LZQ  Date: 2016/05/03 ProjectName: paycore Version: 5.0.0
 */
@Slf4j
@Service
public class WebConsumerHandler extends ConsumeHandler {

    @Resource(name = "webBankProcessor")
    private BaseProcessor webBankProcessor;

    /**
     * 异步支付
     *
     * @param payBO 订单对象
     * @return      跳转URL
     */
    @Override
    public WebPayBO asyncProcess(PayBO payBO){
        log.info("web支付开始处理：{}", payBO);
        checkBeforeProcess(payBO);
        processOrders(payBO);
        log.info("web支付完成结果：{}", payBO);
        return BO2BOConverter.payBO2WebPayBO(payBO);
    }

    /**
     * 生成指令
     *
     * @param order 单据
     */
    @Override
    protected void processOrders(PayBO order) {

        List<Command> commands = payCommandGenerator.generatorCommands(order);
        Collections.sort(commands);
        log.info("支付指令：{}", commands);
        if((!PayToolRouteEnum.CLEAR_WEBPAY_ROUTE.containsPayTool(commands.get(0).getPayToolType()))){
            log.error("支付顺序有误");
            throw new BizServiceException(ServiceErrorCode.PROCESS_ORDER_ERROR);
        }
        Map<String, CommandResult> resultMap = webBankProcessor.doProcess(commands.get(0));
        log.debug("web 支付前台结果:{}", resultMap);

        CommandResult commandResult = resultMap.get(commands.get(0).getCommandNo());
        processAfter(order, resultMap);
        order.setRealFlag(commandResult.isRealRtnFlag());
        if (Objects.equals(PayStatusEnums.FAIL.getCode(), commandResult.getStatus())) {
            setSubPayFailStatus(order);
            reStore(order);
            return;
        }
        //设置url
        WebPayResDTO resDTO = (WebPayResDTO) commandResult.getResultVO();
        order.setStatus(resDTO.getStatus());
        order.setOutPayUrl(resDTO.getOutPayUrl());
        order.setParams(resDTO.getParams());
    }

    /**
     * 回调
     *
     * @param callBack          充值结果
     * @return 回调处理响应
     */
    @Override
    public PayBO callBack(RechargeBO callBack) {
        //1.查询原交易
        PayBO payBO = activeOrder(callBack.getRequestOrderNo());
        payBO.setTradeLogId(callBack.getTradeLogId());

        //支付成功，直接返回
        if(PayStatusEnums.SUCCESS.getCode().equals(payBO.getStatus())){
            return payBO;
        }

        //2.设置清分单状态,判断是否继续 账务充值
        if(!checkRecharge(callBack,payBO)){
            reStore(payBO);
            return payBO;
        }

        //3.生成后续资金处理指令
        List<Command> commands = payCommandGenerator.generatorCommands(payBO);
        Collections.sort(commands);
        log.info("支付指令：{} {}", commands.size(), commands);
        Command command = commands.remove(0);
        validate(command, callBack);
        log.info("支付指令 条数：{}", commands.size());

        //4.处理其他支付单
        Map<String, CommandResult> cmdResults = commandExecutor.executeCommand(commands);
        log.info("支付指令 条数：{}", commands.size());

        //5.设置订单状态
        processAfter(payBO, cmdResults);
        reStore(payBO);
        return payBO;
    }

    /**
     * 校验是否继续处理其他资金单
     *
     * @param callBack          充值回调
     * @param payBO             支付订单
     * @return                  结果
     */
    private boolean checkRecharge(RechargeBO callBack,PayBO payBO){
        log.info("充值结果, 状态: {}", callBack.getStatus());
        SubPayBO subPayBO = payBO.getFundDetail(payBO.getSubPayList(), callBack.getRequestBizNo());
        setBankResp(callBack,subPayBO);
        if(Objects.equals(PayStatusEnums.SUCCESS.getCode(),callBack.getStatus())){
            return true;
        }
        //设置订单状态为失败
        payBO.setStatus(PayStatusEnums.FAIL.getCode());
        payBO.setErrorCode(callBack.getErrorCode());
        payBO.setErrorMsg(callBack.getErrorMsg());
        setSubPayFailStatus(payBO);
        return false;
    }

    /**
     * 设置支付子单为失败
     *
     * @param payBO     支付单
     */
    private void setSubPayFailStatus(PayBO payBO){
        for (SubPayBO bo : payBO.getSubPayList()) {
            bo.setStatus(PayStatusEnums.FAIL.getCode());
        }
    }
    /**
     * 回调银行单校验
     *
     * @param command           第一条支付指令
     * @param rechargeBO        充值结果
     */
    private void validate(Command command,RechargeBO rechargeBO){
        if(!(command instanceof BankCommand)){
            log.error("回调资金单排序有误:{}",command);
            throw new BizServiceException(ServiceErrorCode.PROCESS_ORDER_ERROR);
        }
        if((!PayToolRouteEnum.CLEAR_WEBPAY_ROUTE.containsPayTool(command.getPayToolType()))||
                !Objects.equals(rechargeBO.getRequestBizNo(),((BankCommand) command).getRequestBizNo()) ){
            log.error("回调资金单校验有误:{}",command);
            throw new BizServiceException(ServiceErrorCode.PROCESS_ORDER_ERROR);
        }
    }

    /**
     * 银行单赋值
     *
     * @param callBack      充值回调
     * @param subPayBO      银行单
     */
    private void setBankResp(RechargeBO callBack,SubPayBO subPayBO){
        subPayBO.setStatus(callBack.getStatus());
        subPayBO.setErrorCode(callBack.getErrorCode());
        subPayBO.setErrorReason(callBack.getErrorMsg());
        subPayBO.setTransDate(callBack.getRespDate());
        subPayBO.setTransReceiptNo(callBack.getPayNo());
    }
}
